/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package app;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.*;
import javax.swing.*;

/**
 *
 * @author CygiDawid
 */
public class CarTestFrame extends javax.swing.JFrame {
    private static final double RADIANS_CALC = Math.PI / 180d;
    private static final String FPS_FORMAT = "%.0f";
    private Image _defaultCar;
    private BufferedImage _car;
    private BufferedImage _background;
    private BufferedImage _buffer;
    private MyUpdaterTask _updaterTask = new MyUpdaterTask();
    private Thread _updater = new Thread(_updaterTask);
    private double _fps = 0d;
    
    private final int MAX_FPS = 30;
    private final int FRAME_TIME = 1000 / MAX_FPS;
    private final double TOP_SPEED = 15d;
    private final double TOP_BACK_SPEED = -5d;
    private final double ROTATION_STEP = 5d;
    private final double BREAKS = 0.5d;
    private final double ACCELERATION = 0.3d;
    private final double FREE_SLOW_DOWN = 0.1d;
    private final double ROTATION = ROTATION_STEP / TOP_SPEED;
    private double _speed = 0d;
    private double _angle = 0d;
    private double _posX = 100d;
    private double _posY = 100d;
    private boolean _up = false;
    private boolean _down = false;
    private boolean _left = false;
    private boolean _right = false;
    
    
    private class MyUpdaterTask implements Runnable {
        private boolean _isRunning = true;
        private Date _lastRefresh;
        
        
        public void terminate() {
            _isRunning = false;
        }
        
        @Override
        public void run() {
            while (_isRunning)
            {
                Date now = new Date();
                if (_lastRefresh != null) {
                    _fps = 1000d / (double)(now.getTime() - _lastRefresh.getTime());
                }
                _lastRefresh = now;
                
                refreshGame();
                
                try
                {
                    long sleepTime = FRAME_TIME - (new Date().getTime() - now.getTime());
                    if (sleepTime > 0) {
                        Thread.sleep(sleepTime);
                    }
                }
                catch(InterruptedException ie) { }
            }
        }
    };
    
    private class MyKeyListener  implements KeyListener {
        @Override
        public synchronized void keyTyped(KeyEvent e) {
            
        }
        
        @Override
        public synchronized void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            if (key == KeyEvent.VK_LEFT) {
                _left = true;
            }
            if (key == KeyEvent.VK_RIGHT) {
                _right = true;
            }
            if (key == KeyEvent.VK_UP) {
                _up = true;
            }
            if (key == KeyEvent.VK_DOWN) {
                _down = true;
            }
        }
        
        @Override
        public synchronized void keyReleased(KeyEvent e) {
            int key = e.getKeyCode();
            if (key == KeyEvent.VK_LEFT) {
                _left = false;
            }
            if (key == KeyEvent.VK_RIGHT) {
                _right = false;
            }
            if (key == KeyEvent.VK_UP) {
                _up = false;
            }
            if (key == KeyEvent.VK_DOWN) {
                _down = false;
            }
        }
    }
    
    
    /**
     * Creates new form CarTestFrame
     */
    public CarTestFrame() {
        initComponents();
        
        addKeyListener(new MyKeyListener());
        
        createGraphics();
        
        _posX = (int)((getWidth() - _car.getWidth()) * 0.5d);
        _posY = (int)((getHeight() - _car.getHeight()) * 0.5d);
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setUndecorated(true);
        setPreferredSize(new java.awt.Dimension(1920, 1080));
        setResizable(false);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 1158, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 629, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents
    
    public void start() {
        _updater.start();
    }
    
    public void stop() {
        try {
            _updaterTask.terminate();
            _updater.join();
        } catch (Exception e) { System.err.println(e.getStackTrace()); }
        
        _updaterTask = new MyUpdaterTask();
        _updater = new Thread(_updaterTask);
    }
    
    
    private void createGraphics() {
        _defaultCar = (new ImageIcon(getClass().getResource("car-mini.png"))).getImage();
        rotateCar();        // tworzy obrazek samochodu
        
        _buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        Graphics g = _buffer.getGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, getWidth(), getHeight());
        
        _background = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
        g = _background.getGraphics();
        g.setColor(Color.black);
        g.fillRect(0, 0, getWidth(), getHeight());
    }
    
    private void refreshGame() {
        steer();
        
        double radians = _angle * RADIANS_CALC;
        double ax = Math.sin(radians) * _speed;
        double ay = Math.cos(radians) * _speed;
        _posX += ax;
        _posY -= ay;
        
        repaint();
    }
    
    private void steer() {
        if (_left) {
            _angle -= ROTATION * _speed;
            rotateCar();
        }
        if (_right) {
            _angle += ROTATION * _speed;
            rotateCar();
        }
        if (_up) {
            if (_speed > 0d) {
                _speed += ACCELERATION;
                
                if (_speed > TOP_SPEED) {
                    _speed = TOP_SPEED;
                }
            }
            else {
                _speed += BREAKS;
            }
        }
        if (_down) {
            if (_speed < 0d) {
                _speed -= ACCELERATION;
                
                if (_speed < TOP_BACK_SPEED) {
                    _speed = TOP_BACK_SPEED;
                }
            }
            else {
                _speed -= BREAKS;
            }
        }
        
        if (!_up && !_down) {
            if (_speed > 0d) {
                _speed -= FREE_SLOW_DOWN;
            }
            else if (_speed < 0d) {
                _speed += FREE_SLOW_DOWN;
            }
            
            if (Math.abs(_speed) <= FREE_SLOW_DOWN) {
                _speed = 0d;
            }
        }
    }
    
    private void rotateCar() {
        _car = new BufferedImage(_defaultCar.getWidth(null), _defaultCar.getHeight(null), BufferedImage.TYPE_4BYTE_ABGR);
        Graphics2D gr = (Graphics2D)_car.getGraphics();
        gr.rotate(Math.toRadians(_angle), _car.getWidth() * 0.5d, _car.getHeight() * 0.5d);
        gr.drawImage(_defaultCar, 0, 0, _defaultCar.getWidth(null), _defaultCar.getHeight(null), null);
    }
    
    @Override
    public synchronized void paint(Graphics g) {
        update(g);
    }
    
    @Override
    public synchronized void update(Graphics gr) {
        gr.drawImage(_buffer, 0, 0, null);
        
        Graphics g = _buffer.getGraphics();
        g.drawImage(_background, 0, 0, null);       // odrysowanie tła (później tłem będzie trasa)
        g.setColor(Color.white);
        g.drawString(String.format(FPS_FORMAT, _fps), 5, 38);
        
        g.drawImage(_car, (int)_posX, (int)_posY, null);
        
        getToolkit().sync();
    }
    
        
        
   
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(CarTestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(CarTestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(CarTestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(CarTestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                CarTestFrame form = new CarTestFrame();
                form.setVisible(true);
                form.start();
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables
}
